﻿using System;
using Plugins.Base.Common;
using Plugins.Common;
using Plugins.UserControls;
using StockModel;
using TechnicalIndicators.DateBased;
using TechnicalIndicators.QuotationBased;
using Utils;

namespace TrixPlugin.Common
{
	internal class DecisionEngine : Plugins.Base.Common.DecisionEngineBase
	{
		#region Attributes

		private readonly HourSelectorData _hourSettings;
		private readonly StrategyControlData _strategySettings;

		private readonly BarGenerator _barGenerator;
		private readonly LastValueGenerator _lastValueGenerator;
		private readonly BarValueExtractor _barValueExtractor;
		private readonly Trix _trixGenerator;

		private readonly IDecisionEngineStateProcessor _processor;

		#endregion

		#region Properties

		public new DecisionEngineData Data
		{
			get
			{
				return (DecisionEngineData)base.Data;
			}
		}

		#endregion

		#region Public Methods

		public DecisionEngine(DecisionEngineData data, ObservableCollection2<Quotation> quotationSource, StrategyControlData strategySettings, HourSelectorData hourSettings, OperationManager operationManager) : base(data, quotationSource, operationManager)
		{
			_strategySettings = strategySettings;
			_hourSettings = hourSettings;

			_barGenerator = new BarGenerator();
			_lastValueGenerator = new LastValueGenerator();
			_barValueExtractor = new BarValueExtractor();
			_trixGenerator = new Trix();

			_lastValueGenerator.Source = new ReadWriteLockedObservableCollection<Quotation>(quotationSource, TimeSpan.FromSeconds(10));
			_lastValueGenerator.Configure(true);

			_barGenerator.Source = _lastValueGenerator.Serie;
			_barGenerator.Configure(TimeSpan.FromSeconds(_strategySettings.BarPeriod));

			_barValueExtractor.Source = _barGenerator.Serie;
			_barValueExtractor.Configure(BarToValueMode.Average);

			_trixGenerator.Source = _barValueExtractor.Serie;
			_trixGenerator.Configure(_strategySettings.TrixPeriod);

			_lastValueGenerator.Start();
			_barGenerator.Start();
			_barValueExtractor.Start();
			_trixGenerator.Start();

			if (_strategySettings.WorkingMode == OperationMode.OnZeroCross)
			{
				_processor = new ZeroCrossProcessor(_trixGenerator, _strategySettings, _hourSettings, this.Data, base.OperationManager);	
			}
			else
			{
				_processor = new DirectionChangeProcessor(_trixGenerator, _strategySettings, _hourSettings, this.Data, base.OperationManager);	
			}

			base.RegisterForNotifications();
		}

		protected override void EvaluateStateMachineInternal()
		{
			this.SetState(this.Data.State);

			if (_trixGenerator.LastValue != null)
			{
				this.Data.PreviousTrix = _trixGenerator.LastValue.Y;
			}
		}

		#endregion

		#region Private Methods

		public void SetState(DecisionEngineStateBase state)
		{
			this.Data.State = state;

			DecisionEngineStateBase newState = null;

			if (state == DecisionEngineState.WaitingOpen)
			{
				newState = this.ProcessWaitingOpen();
			}
			else if (state == DecisionEngineState.WaitingDirection)
			{
				newState = _processor.ProcessWaitingDirection();
			}
			else if (state == DecisionEngineState.OnLongPossition)
			{
				newState = _processor.ProcessOnLongPossition();
			}
			else if (state == DecisionEngineState.OnShortPossition)
			{
				newState = _processor.ProcessOnShortPossition();
			}
			else if (state == DecisionEngineState.DayClosed)
			{
				newState = this.ProcessDayClosed();
			}

			if (newState != null)
			{
				if ((newState == DecisionEngineState.OnLongPossition && this.Data.State != DecisionEngineState.OnLongPossition) || (newState == DecisionEngineState.OnShortPossition && this.Data.State != DecisionEngineState.OnShortPossition))
				{
					this.Data.State = newState; // Do not evaluate these steps after creating them. If the gap is too big we can enter an endless loop between them.
				}
				else
				{
					this.SetState(newState);
				}
			}
		}

		private DecisionEngineStateBase ProcessWaitingOpen()
		{
			DecisionEngineStateBase result = null;

			if (this.Data.LastQuotation.Date.TimeOfDay > _hourSettings.LastOpen)
			{
				result = DecisionEngineState.DayClosed;
			}
			else if (this.Data.LastQuotation.Volume > 0)
			{
				result = DecisionEngineState.WaitingDirection;
			}

			return result;
		}

		

		private DecisionEngineStateBase ProcessDayClosed()
		{
			DecisionEngineStateBase result = null;

			if (this.Data.LastQuotation.Date.TimeOfDay < _hourSettings.LastOpen && (this.Data.LastQuotation.Date.Date != this.Data.PreviousQuotation.Date.Date || this.Data.LastQuotation.Date < this.Data.PreviousQuotation.Date))
			{
				this.Data.PreviousQuotation = null;
				this.Data.PreviousTrix = null;

				_lastValueGenerator.Clear();
				_lastValueGenerator.Start();

				_barGenerator.Clear();
				_barGenerator.Start();

				_barValueExtractor.Clear();
				_barValueExtractor.Start();

				_trixGenerator.Clear();
				_trixGenerator.Start();

				result = DecisionEngineState.WaitingOpen;
			}

			return result;
		}

		#endregion

	}
}
